<!--
Tests that AR hit test results are available in rAF as soon as hit test source is available.
-->
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
  </head>
  <body>
    <canvas id="webgl-canvas"></canvas>
    <script src="../../../../../../third_party/blink/web_tests/resources/testharness.js"></script>
    <script src="../resources/webxr_e2e.js"></script>
    <script>var shouldAutoCreateNonImmersiveSession = false;</script>
    <script src="../resources/webxr_boilerplate.js"></script>
    <script>
      setup({single_test: true});

      const TestState = Object.freeze({
        "Initial":  0,
        "RequestHitTestSource": 1,
        "HitTestSourceRequested": 2,
        "HitTestSourceAvailable": 3,  // hitTestSource variable is guaranteed to be non-null in this state
        "Done": 4,
      });

      let testState = null;

      let hitTestSource = null;

      function stepStartHitTesting() {
        const sessionInfo = sessionInfos[sessionTypes.AR];
        const referenceSpace = sessionInfo.currentRefSpace;

        testState = TestState.Initial;

        // There is no way we can guarantee that the recording starts with a plane detected - the
        // recording only contains sensors stream and camera images, not the ARCore session state
        // itself. ARCore should work in exactly the same way as if it is a brand new session, so
        // it will go through "camera not tracking" state at the beginning, on to detecting some
        // feature points, and then to actually finding the plane. Given that, we should allow
        // for retry logic in the test - the initial hit tests may return empty results until
        // ARCore detects a plane.
        // Since we try every frame and we currently run at 30FPS, it should mean we'll retry for
        // 5 seconds.
        let retries_left = 30 * 5;

        onARFrameCallback = (session, frame) => {
          switch(testState) {

            case TestState.Initial: {
              const pose = frame.getViewerPose(referenceSpace);
              if(!pose || pose.emulatedPosition) {
                // Wait for session to stabilize.
                return;
              }

              testState = TestState.RequestHitTestSource;
              return;
            }
            case TestState.RequestHitTestSource: {
              // Try to create a hit test source from within a frame.
              sessionInfo.currentSession.requestHitTestSource({
                space: referenceSpace,
                offsetRay: new XRRay()
              }).then((hts) => {
                hitTestSource = hts;
                testState = TestState.HitTestSourceAvailable;
              }).catch((err) => {
                assert_unreached("XRSession.requestHitTestSource() promise rejected.");
                testState = TestState.Done;
              });

              testState = TestState.HitTestSourceRequested;

              return;
            }
            case TestState.HitTestSourceAvailable: {
              const results = frame.getHitTestResults(hitTestSource);
              if(results.length) {
                testState = TestState.Done;
                done();
              } else {
                --retries_left;
                if(retries_left > 0) {
                  // Retry.
                  testState = TestState.RequestHitTestSource;
                } else {
                  assert_unreached("Hit test results were not available right after the hit test source was created.");
                  testState = TestState.Done;
                }
              }

              // Irrespective of the results, we won't need this hit test source anymore.
              hitTestSource.cancel();
              hitTestSource = null;

              return;
            }
            default:
              return;
          }
        };
      }
    </script>
  </body>
</html>
